package com.syx.system.Service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.syx.commoncore.Constant.Constant;
import com.syx.commoncore.Model.Result;
import com.syx.commoncore.Model.TableDataInfoResult;
import com.syx.commoncore.enums.Resultcode;
import com.syx.security.Exception.ServiceException;
import com.syx.system.Manager.ExamCacheManager;
import com.syx.system.Mapper.exam.ExamMapper;
import com.syx.system.Mapper.exam.ExamQuestionMapper;
import com.syx.system.Mapper.question.QuestionMapper;
import com.syx.system.Model.exam.DTO.ExamAddDTO;
import com.syx.system.Model.exam.DTO.ExamEditDTO;
import com.syx.system.Model.exam.DTO.ExamQueryDTO;
import com.syx.system.Model.exam.DTO.ExamQuestionAddDTO;
import com.syx.system.Model.exam.Exam;
import com.syx.system.Model.exam.ExamQuestion;
import com.syx.system.Model.exam.VO.ExamDetailVO;
import com.syx.system.Model.exam.VO.ExamVO;
import com.syx.system.Model.question.Question;
import com.syx.system.Model.question.VO.QuestionQueryVO;
import com.syx.system.Service.IExamService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@Slf4j
@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {

    @Resource
    ExamMapper examMapper;

    @Resource
    QuestionMapper questionMapper;

    @Resource
    ExamQuestionMapper examQuestionMapper;

    @Resource
    ExamCacheManager examCacheManager;


    @Override
    public TableDataInfoResult list(ExamQueryDTO examQueryDTO) {
        if (examQueryDTO.getPageNum()==null|| examQueryDTO.getPageSize()==null){
            examQueryDTO.setPageNum(1);
            examQueryDTO.setPageSize(10);
        }
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());
        List<Exam> examList;
        List<ExamVO> examVO =new ArrayList<>();
        if (Objects.equals(examQueryDTO.getType(), Constant.TRUE)) {
            // 查询历史竞赛：结束时间小于当前时间
            examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                    .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime,Exam::getStatus)
                    .lt(Exam::getEndTime, LocalDateTime.now())
                    .eq(Exam::getStatus, Constant.TRUE)
                    .orderByDesc(Exam::getCreateTime));
            for (Exam exam : examList) {
                ExamVO examVOList = new ExamVO();
                BeanUtils.copyProperties(exam, examVOList);
                examVO.add(examVOList);
            }
        } else if (Objects.equals(examQueryDTO.getType(), Constant.FALSE)) {
            // 查询进行中或未开始的竞赛：开始时间大于等于当前时间
            examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                    .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime,Exam::getStatus)
                    // 修改为结束时间大于当前时间
                    .gt(Exam::getEndTime, LocalDateTime.now())
                    .eq(Exam::getStatus, Constant.TRUE)
                    .orderByDesc(Exam::getCreateTime));
            for (Exam exam : examList) {
                ExamVO examVOList = new ExamVO();
                BeanUtils.copyProperties(exam, examVOList);
                examVO.add(examVOList);
            }
        }else {
            examVO = examMapper.selectExamList(examQueryDTO);
        }

        return TableDataInfoResult.success(examVO,new PageInfo<>(examVO).getTotal());
    }

    @Override
    public Result<String> add(ExamAddDTO examAddDTO) {
        Exam exam = checkExam(examAddDTO);


        BeanUtil.copyProperties(examAddDTO,exam);
        int row = examMapper.insert(exam);
        if (row!=1){
            throw new ServiceException(Resultcode.FAILED_INSERT);

        }


        return Result.success(exam.getExamId().toString());




    }



    @Override
    public Result<Void> questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        //先看一看竞赛是否存在,在添加题目的时候
        Exam exitsExam = isExitsExam(examQuestionAddDTO);
        //竞赛开始的时候不能操作
        if (Constant.TRUE.equals(exitsExam.getStatus())){
            throw new ServiceException(Resultcode.EXAM_IS_PUBLISHED);
        }

        Set<Long> questionId = examQuestionAddDTO.getQuestionId();
        //查看题目是否存在
        if (CollectionUtil.isEmpty(questionId)){
            saveExamQuestion(examQuestionAddDTO);
            return Result.success(exitsExam.getExamId());
        }
        List<Question> batchQuestions = questionMapper.selectBatchIds(questionId);


        if (CollectionUtil.isEmpty(batchQuestions) || batchQuestions.size()<examQuestionAddDTO.getQuestionId().size()){
            throw new ServiceException(Resultcode.EXAM_QUESTION_NOT_EXIST);
        }
        //插入是否成功
        Boolean isSaveExamQuestion = saveExamQuestion(examQuestionAddDTO);
        if (isSaveExamQuestion){
            return Result.success(exitsExam.getExamId());
        }

        return Result.failure();
    }

    @Override
    public Result<ExamDetailVO> detail(Long examId) {
        Exam exitsExam = getsExam(examId);
        ExamDetailVO examDetailVO=new ExamDetailVO();
        examDetailVO.setStartTime(exitsExam.getStartTime());
        examDetailVO.setEndTime(exitsExam.getEndTime());
        examDetailVO.setTitle(exitsExam.getTitle());
        //获取数据库中questionId字段
        List<Long> questionIds = examQuestionMapper.getQuestionIdsByExamId(examId);
        if (CollectionUtil.isEmpty(questionIds)){
            //如果为空的情况
            return Result.success(examDetailVO);
        }
        System.out.println(questionIds);
        List<Question> questions = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getDifficulty, Question::getTitle)
                .in(Question::getQuestionId, questionIds));
        System.out.println(questions);
        List<QuestionQueryVO> queryVOList=new ArrayList<>();
        //将查出来的question数据转到queryVOList中
        List<QuestionQueryVO> questionQueryVO = BeanUtil.copyToList(questions, QuestionQueryVO.class);
        System.out.println(questionQueryVO);
        examDetailVO.setExamQuestionList(questionQueryVO);

        return Result.success(examDetailVO);

    }

    @Override
    public Result<Void> edit(ExamEditDTO examEditDTO) {
        Exam updateExam = getsExam(examEditDTO.getExamId());
        if (Constant.TRUE.equals(updateExam.getStatus())){
            throw new ServiceException(Resultcode.EXAM_IS_PUBLISHED);
        }
        isExamBegin(updateExam);
        //对竞赛题目进行判断,并且把所有的数据复制到exam中，但是examedit中有些数据为空不能使用
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        updateExam.setTitle(examEditDTO.getTitle());
        updateExam.setStartTime(examEditDTO.getStartTime());
        updateExam.setEndTime(examEditDTO.getEndTime());
        int row = examMapper.updateById(updateExam);
        return Result.success();
    }

    @Override
    public Result<Void> questionDelete(Long examId, Long questionId) {
        //
        Exam exam = getsExam(examId);
        if (Constant.TRUE.equals(exam.getStatus())){
            throw new ServiceException(Resultcode.EXAM_IS_PUBLISHED);
        }
        //如果竞赛已经开始了,进行判断
        isExamBegin(exam);
        //可以 操作
        int deleteRow = examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
        //没有找到匹配的
        if (deleteRow==0) {
            //删除失败的情况
            throw new ServiceException(Resultcode.FAILED);


        }
        return Result.success();


    }

    @Override
    public Result<Void> delete(Long examId) {
        //获取竞赛并且查看竞赛是否存在
        Exam exam = getsExam(examId);
        //查看竞赛是否开始
        isExamBegin(exam);
        int examRow = examMapper.deleteById(examId);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId,examId));
        if (examRow==0){
            throw new ServiceException(Resultcode.FAILED);
        }
        return Result.success();


    }

    @Override
    public Result<Void> publish(Long examId) {
        Exam exam = getsExam(examId);
        //如果事件已经到了就不能发布竞赛
        if (exam.getEndTime().isBefore(LocalDateTime.now())){
            //绕过前端进行调用
            throw new ServiceException(Resultcode.EXAM_NOT_EXIST);
        }


        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        if (count==null || count<=0){
            //在这边没有找到对应的
            throw new ServiceException(Resultcode.EXAM_NOT_HAS_QUESTION);

        }
        //如果有关联题目的话就把status设置为1
        exam.setStatus(Constant.TRUE);
        ///将数据存储到redis中 key: e:t:l  value: examId，操作redis
        examCacheManager.addCache(exam);
        //对已经结束的不能进行撤销发布


        int updatedRow = examMapper.updateById(exam);
        if (updatedRow!=1){
            throw new ServiceException(Resultcode.PUBLISH_FAILURE);

        }
        return Result.success();



    }

    @Override
    public Result<Void> cancelPublish(Long examId) {
        Exam exam = getsExam(examId);
        //竞赛已经结束既不能撤销发布也不能发布竞赛
        if (exam.getEndTime().isBefore(LocalDateTime.now())){
            //绕过前端进行调用
            throw new ServiceException(Resultcode.EXAM_NOT_EXIST);
        }
        isExamBegin(exam);
        exam.setStatus(Constant.FALSE);
        examCacheManager.deleteCache(exam.getExamId());
        int updateRow = examMapper.updateById(exam);
        if (updateRow!=1){
            throw new ServiceException(Resultcode.PUBLISH_FAILURE);

        }
        return Result.success();

    }



    private static void isExamBegin(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(Resultcode.EXAM_STARTED);

        }
    }

    //添加竞赛时进行题目选择
    private Boolean saveExamQuestion(ExamQuestionAddDTO examQuestionAddDTO) {
        //获取
        if (examQuestionAddDTO.getQuestionId()==null){
            examQuestionMapper.deleteByExamId(examQuestionAddDTO.getExamId());
            return saveBatch(new ArrayList<>());
        }
        examQuestionMapper.deleteByExamId(examQuestionAddDTO.getExamId());

        int num=1;
        List<ExamQuestion> examQuestionList=new ArrayList<>();
        for(Long questionId: examQuestionAddDTO.getQuestionId()){
            ExamQuestion examQuestion=new ExamQuestion();
            examQuestion.setQuestionId(questionId);
            examQuestion.setExamId(examQuestionAddDTO.getExamId());
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);


        }
        //通过继承examquestion这个类就知道要插入哪个表了
        return saveBatch(examQuestionList);
    }


    //
    private Exam isExitsExam(ExamQuestionAddDTO examQuestionAddDTO) {
        Exam isExistExam = examMapper.selectById(examQuestionAddDTO.getExamId());
        if (isExistExam==null){

            throw new ServiceException(Resultcode.EXAM_NOT_EXIST);
        }
        return isExistExam;
    }
    private Exam getsExam(Long examId) {
        Exam isExistExam = examMapper.selectById(examId);
        if (isExistExam==null){

            throw new ServiceException(Resultcode.EXAM_NOT_EXIST);
        }
        return isExistExam;
    }


    //查看竞赛中是否有相同的竞赛名字，有的话就进行检验并且将examAddDTO中的值复制
    private Exam checkExam(ExamAddDTO examAddDTO) {

        if (examAddDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(Resultcode.EXAM_EARLY_START);
        }
        //结束时间早于开始时间
        if (examAddDTO.getEndTime().isBefore(examAddDTO.getStartTime()) || examAddDTO.getEndTime().isEqual(examAddDTO.getStartTime())){
            throw new ServiceException(Resultcode.EXAM_LATE_BEFORE_OVER);
        }
        Exam exam=new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);
        int count = examMapper.mySelectCount(exam.getTitle());
        if (count!=0){
            //如果查到了相同的竞赛题目标题则报错，发出异常
            throw new ServiceException(Resultcode.FAILED_ALREADY_EXISTS);

        }
        return exam;
    }
    private void checkExamSaveParams(ExamAddDTO examSaveDTO, Long examId) {
        //1、竞赛标题是否重复进行判断   2、竞赛开始、结束时间进行判断
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId != null, Exam::getExamId, examId));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(Resultcode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(Resultcode.EXAM_LATE_BEFORE_OVER);  //竞赛开始时间不能早于当前时间
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(Resultcode.EXAM_EARLY_START);
        }
    }



}
